#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "block.h"

#define BLOCK_SIZE 4096

struct block{
    block_t  *next;
    block_t  *prev;
    uint8_t  *read_ptr;     /* 当前读指针 */
    uint8_t  *write_ptr;    /* 当前写指针 */
    uint8_t  *buf_end;
    uint8_t  buf_base[];
};

block_t *block_new(size_t size)
{
    block_t *blk = NULL;
    size = (size + BLOCK_SIZE -1 )/BLOCK_SIZE * BLOCK_SIZE;

    blk = malloc(sizeof(block_t) + size);
    if (blk != NULL) {
        blk->next = blk;
        blk->prev = blk;
        blk->read_ptr  = blk->buf_base;
        blk->write_ptr = blk->buf_base;
        blk->buf_end   = blk->buf_base + size;
    }
    return blk;
}

void block_delete(block_t *blk)
{
    block_t *entry = blk;
    block_t *next = NULL;
    do {
        next = blk->next;
        free(blk);
        blk = next;
    } while(blk != entry);

}

void block_reset(block_t *blk)
{
    blk->read_ptr  = blk->buf_base;
    blk->write_ptr = blk->buf_base;
}

inline block_t *block_next(block_t *blk)
{
    return blk->next;
}

inline block_t *block_prev(block_t *blk)
{
    return blk->prev;
}

void block_append(block_t *blk_cur, block_t *blk)
{
    blk->next = blk_cur->next;
    blk->prev = blk_cur;
    blk_cur->next = blk;
}

void block_remove(block_t *blk)
{
    block_t *prev = blk->prev;

    if (prev != blk) {
        prev->next = blk->next;
        blk->next->prev = prev;
        free(blk);
    }
}

inline size_t block_get_free_size(block_t *blk)
{
    return (blk->buf_end - blk->write_ptr);
}

inline size_t block_get_used_size(block_t *blk)
{
    return (blk->write_ptr - blk->read_ptr);
}

inline ssize_t block_read(block_t *blk, uint8_t *rbuf, size_t len)
{
    size_t rlen = block_get_used_size(blk);

    if (rlen > 0) {
        if (rlen > len) {
            rlen = len;
        }
        memcpy(rbuf, blk->read_ptr, rlen);
        blk->read_ptr += rlen;
    }
    return rlen;
}

inline ssize_t block_write(block_t *blk, uint8_t *wbuf, size_t len)
{
    size_t wlen = block_get_free_size(blk);
    if (wlen > 0) {
        if (wlen > len) {
            wlen = len;
        }
        memcpy(blk->write_ptr, wbuf, wlen);
        blk->write_ptr += wlen;
    }
    return wlen;
}

int32_t block_isreadable(block_t *blk)
{
    return (blk->read_ptr != blk->buf_end);
}

int32_t block_iswriteable(block_t *blk)
{
    return (blk->write_ptr != blk->buf_end);
}
